﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Ukagaka.Named
{
	/// <summary>
	/// キー値ペア形式設定の実装です
	/// </summary>
	public class SimpleSetting
	{
		/// <summary>
		/// キーと値のコレクションを取得します
		/// </summary>
		private readonly Dictionary<string, string> Dictionary = new Dictionary<string, string>( );

		/// <summary>
		/// キー値ペア形式設定のエンコーディングを取得または設定します
		/// </summary>
		public Encoding Encoding
		{
			get { return GetValue( "charset", SimpleSettingFile.DefaultEncoding, Encoding_Selector ); }
			set { SetValue( "charset", value, Encoding_Selector ); }
		}

		/// <summary>
		/// キー値ペア形式設定の名称を取得します
		/// </summary>
		public virtual string Name
		{
			get { return GetValue( "name" ); }
			set { SetValue( "name", value ); }
		}

		/// <summary>
		/// キー値ペア形式設定の種別を取得します
		/// </summary>
		public SimpleSettingType SettingType
		{
			get { return GetValue( "type", SettingType_Selector ); }
			set { SetValue( "type", value, SettingType_Selector ); }
		}

		/// <summary>
		/// キー値ペア形式設定を初期化します
		/// </summary>
		public SimpleSetting( ) { /* 処理なし */ }

		/// <summary>
		/// キー値ペア形式設定を読み込みます
		/// </summary>
		/// <param name="path">キー値ペア形式設定ファイル</param>
		/// <remarks>既に読み込まれている値は全てクリアされます</remarks>
		public void Load( string path )
		{
			Clear( );
			try
			{
				foreach ( var pair in SimpleSettingFile.ReadLines( path ) )
				{
					Dictionary[ pair.Key ] = pair.Value;
				}
			}
			catch
			{
				Clear( );
				throw;
			}
		}

		/// <summary>
		/// キー値ペア形式設定をクリアします
		/// </summary>
		private void Clear( ) { Dictionary.Clear( ); }

		/// <summary>
		/// キーの値を取得します
		/// </summary>
		/// <param name="key">キー</param>
		/// <param name="value">値</param>
		/// <returns>正常に取得できた場合はtrue、それ以外の場合はfalse</returns>
		protected bool TryGetValue( string key, out string value ) { return Dictionary.TryGetValue( key, out value ); }

		/// <summary>
		/// キーの値を取得します
		/// </summary>
		/// <param name="key">キー</param>
		/// <returns>正常に取得できた場合はキーの値、それ以外の場合はnull</returns>
		protected string GetValue( string key ) { return GetValue( key, default( string ) ); }

		/// <summary>
		/// キーの値を取得します
		/// </summary>
		/// <param name="key">キー</param>
		/// <param name="def">既定値</param>
		/// <returns>正常に取得できた場合はキーの値、それ以外の場合は既定値</returns>
		protected string GetValue( string key, string def ) { return GetValue( key, def, x => x ); }

		/// <summary>
		/// キーの値を取得します
		/// </summary>
		/// <typeparam name="TResult">値の型</typeparam>
		/// <param name="key">キー</param>
		/// <param name="selector">値のセレクタ</param>
		/// <returns>正常に取得できた場合はセレクタの返却値、それ以外の場合は値の型の既定値</returns>
		protected TResult GetValue<TResult>( string key, Func<string, TResult> selector ) { return GetValue( key, default( TResult ), selector ); }

		/// <summary>
		/// キーの値を取得します
		/// </summary>
		/// <typeparam name="TResult">値の型</typeparam>
		/// <param name="key">キー</param>
		/// <param name="def">既定値</param>
		/// <param name="selector">値のセレクタ</param>
		/// <returns>正常に取得できた場合はセレクタの返却値、それ以外の場合は既定値</returns>
		protected TResult GetValue<TResult>( string key, TResult def, Func<string, TResult> selector )
		{
			var value = default( string );
			return TryGetValue( key, out value ) ? selector( value ) : def;
		}

		/// <summary>
		/// キーの値を設定します
		/// </summary>
		/// <param name="key">キー</param>
		/// <param name="value">値</param>
		protected void SetValue( string key, string value ) { SetValue( key, value, x => x ); }

		/// <summary>
		/// キーの値を設定します
		/// </summary>
		/// <typeparam name="TValue">値の型</typeparam>
		/// <param name="key">キー</param>
		/// <param name="value">値</param>
		/// <param name="selector">値のセレクタ</param>
		protected void SetValue<TValue>( string key, TValue value, Func<TValue, string> selector )
		{
			if ( null == value )
			{
				Dictionary.Remove( key );
			}
			else
			{
				Dictionary[ key ] = selector( value );
			}
		}

		/// <summary>
		/// キー値ペア形式設定のエンコーディングのセレクタです
		/// </summary>
		/// <param name="value">キー値ペア形式設定のエンコーディング</param>
		/// <returns>キー値ペア形式設定のエンコーディング</returns>
		private static string Encoding_Selector( Encoding value ) { return value.WebName; }

		/// <summary>
		/// キー値ペア形式設定のエンコーディングのセレクタです
		/// </summary>
		/// <param name="value">キー値ペア形式設定のエンコーディング</param>
		/// <returns>キー値ペア形式設定のエンコーディング</returns>
		private static Encoding Encoding_Selector( string value ) { return Encoding.GetEncoding( value ); }

		/// <summary>
		/// キー値ペア形式設定の種別のセレクタです
		/// </summary>
		/// <param name="value">キー値ペア形式設定の種別</param>
		/// <returns>キー値ペア形式設定の種別</returns>
		private SimpleSettingType SettingType_Selector( string value )
		{
			switch ( value )
			{
				case "":
				case "none": return SimpleSettingType.None;
				case "ghost": return SimpleSettingType.Ghost;
				case "supplement": return SimpleSettingType.Supplement;
				case "balloon": return SimpleSettingType.Balloon;
				case "plugin": return SimpleSettingType.Plugin;
				case "headline": return SimpleSettingType.Headline;
				case "calendarskin":
				case "calendar skin": return SimpleSettingType.CalendarSkin;
				case "calendarplugin":
				case "calendar plugin": return SimpleSettingType.CalendarPlugin;
				case "package": return SimpleSettingType.Package;
				default: return SimpleSettingType.Unknown;
			}
		}

		/// <summary>
		/// キー値ペア形式設定の種別のセレクタです
		/// </summary>
		/// <param name="value">キー値ペア形式設定の種別</param>
		/// <returns>キー値ペア形式設定の種別</returns>
		private string SettingType_Selector( SimpleSettingType value )
		{
			switch ( value )
			{
				default:
				case SimpleSettingType.None: return "";
				case SimpleSettingType.Unknown: return "";
				case SimpleSettingType.Ghost: return "ghost";
				case SimpleSettingType.Supplement: return "supplement";
				case SimpleSettingType.Balloon: return "balloon";
				case SimpleSettingType.Plugin: return "plugin";
				case SimpleSettingType.Headline: return "headline";
				case SimpleSettingType.CalendarSkin: return "calendar skin";
				case SimpleSettingType.CalendarPlugin: return "calendar plugin";
				case SimpleSettingType.Package: return "package";
			}
		}
	}
}
